
#include "stdafx.h"
#include "XortStudio.h"
#include "DisassemblerRTF.h"

CDisassemblerRTF::CDisassemblerRTF( XRuntime *pRT )
{
    m_pRT = pRT;
}

CDisassemblerRTF::~CDisassemblerRTF()
{

}

void CDisassemblerRTF::AppendString( const CString& str, DbgLineType lnType, DWORD dwIdx, string *pcsName, symbolref pFunc /*= NULL*/ )
{
    m_csAsmRTF += str;
    CDebugLine *pDbgLine = new CDebugLine( lnType, dwIdx, pcsName ? pcsName->GetBuffer() : NULL, pFunc );
    m_pLineArray->add( pDbgLine );

    if( lnType == dl_AsmCode && pFunc )
    {
        pFunc->AddIP2LineInfo( dwIdx, m_pLineArray->count()-1 ); // store info ip-to-line
    }
}

static void EscapeRTFString( string *pStr, CString& csStrOut )
{
    csStrOut = "";

    char *p = pStr->GetBuffer();
    if( p )
    {
        while( *p )
        {
            switch( *p ) // TODO: verify!!!
            {
            case '\'':  csStrOut += "\\'27";    break;
            case '%':   csStrOut += "\\'25";    break;
            case '{':   csStrOut += "\\{";      break;
            case '}':   csStrOut += "\\}";      break;
            case '@':   csStrOut += "\\@";      break;
            case '"':   csStrOut += "\\\"";     break;
            case '\\':  csStrOut += "\\'5c";    break;
                //case '\r':  csStrOut += "\\'5cr";   break;
                //case '\n':  csStrOut += "\\'5cn";   break;
                //case '~':   csStrOut += "\\~";      break;
                //case '_':   csStrOut += "\\_";      break;
                //case '-':   csStrOut += "\\-";      break;
            default:    csStrOut += *p;         break;
            }

            p++;
        }
    } else {
        csStrOut = "(null)";
    }
}

void CDisassemblerRTF::DisassembleVariable( SymbolRef typeName, symbolref pSym, BOOL bLocal )
{
    CString csFmt, csName;

    if( pSym->m_dwFlags & f_const )
    {
        csFmt.Format( "\\cf2    const %s ", typeName.m_csName.GetBuffer() );
        AppendString( csFmt );

        csName.Format( "\\cf3 %s ", pSym->m_csName.GetBuffer() );

        if( pSym->m_dwType == t_string ) {
            string csVal = xEscapeString( *(m_pRT->get_StringFromPool( pSym->m_dwLiteralIdx )) );
            CString csEscaped;
            EscapeRTFString( &csVal, csEscaped );
            csFmt.Format( "\\cf4 = %s\\par\r\n", csEscaped );
        } else {
            string csVal = xFormatElementValue( pSym->m_Value, 10 );
            CString csEscaped;
            EscapeRTFString( &csVal, csEscaped );
            csFmt.Format( "\\cf4 = %s\\par\r\n", csEscaped );
        }
    } else {
        csFmt.Format( "\\cf2    %s ", typeName.m_csName.GetBuffer() );
        AppendString( csFmt );

        csName.Format( "\\cf3 %s ", pSym->m_csName.GetBuffer() );                
        csFmt = "\\cf4 = ?\\par\r\n";
    }

    AppendString( csName );
    AppendString( csFmt, bLocal ? dl_LocalVariable : dl_Variable, pSym->m_iIndex, &pSym->m_csName );
}

void CDisassemblerRTF::DisassembleFunction( SymbolRef typeName, symbolref pSym, BOOL bLocal )
{
    CString csFmt, csName;

    AppendString( "\\cf1    // --------------------------------------------------------------------------\\par\r\n", dl_Comment, -1, NULL );

    csFmt.Format( "\\cf2    %s ", typeName.m_csName.GetBuffer() );
    AppendString( csFmt );

    csName.Format( "\\cf3 %s", pSym->m_csName.GetBuffer() );                
    AppendString( csName );

    CString csDbg; csDbg.Format( "Disassembling function: %s\n", pSym->m_csName.GetBuffer() );
    GetOutput()->Message( OutputBuild, csDbg );

    AppendString( "\\cf1 ( " );

    SYMBOL_LIST::iterator theArgs = pSym->m_listMembers.end();
    theArgs--;
    symbolref pArg = ( theArgs == NULL ) ? NULL : (Symbol*)theArgs; // for cpp compilation
    while( pArg )
    {
        SymbolRef argType = FindType( m_pRT->get_GlobalSymbolList(), pArg->m_dwType );

        csFmt.Format( "\\cf2 %s ", argType.m_csName.GetBuffer() );
        AppendString( csFmt );

        csFmt.Format( "\\cf3 %s", pArg->m_csName.GetBuffer() );                
        AppendString( csFmt );

        theArgs--;
        pArg = theArgs == NULL ? NULL : (Symbol*)theArgs;

        if( pArg )
            AppendString( "\\cf1 , " );
    }

    AppendString( "\\cf1  ) \\{\\par\r\n", bLocal ? dl_LocalFunction : dl_Function, pSym->m_iIndex, &pSym->m_csName );
    DisassembleCode( pSym );

    csFmt.Format( "\\cf1    \\} // ENDP %s\\par\r\n", csName );
    AppendString( csFmt, dl_Comment, -1, NULL );
}

void CDisassemblerRTF::DisassembleClass( symbolref pCls )
{
    CString csFmt;

    AppendString( "\\cf1    // --------------------------------------------------------------------------\\par\r\n", dl_Comment, -1, NULL );
    csFmt.Format( "\\cf2    struct %s \\cf1 \\{\\par\r\n", pCls->m_csName.GetBuffer() );
    AppendString( csFmt, dl_ObjectDeclaration, pCls->m_iIndex, &pCls->m_csName );

    SYMBOL_LIST::iterator theIter = pCls->m_listMembers.end();
    symbolref pSym = (symbolref)theIter;
    while( pSym )
    {
        SymbolRef typeName = FindType( m_pRT->get_GlobalSymbolList(), pSym->m_dwType );

        if( pSym->m_SymKind == sk_var )
        {
            DisassembleVariable( typeName, pSym, TRUE );
        }
        else
        if( pSym->m_SymKind == sk_obj )
        {
            DisassembleClass( pSym );
        }
        else
        if( pSym->m_SymKind == sk_proc )
        {
            DisassembleFunction( typeName, pSym, TRUE );
        }

        theIter--;
        pSym = theIter != NULL ? (symbolref)theIter : NULL;
    }

    csFmt.Format( "\\cf1    \\} // EOF %s --------------------------------------------------------------\\par\r\n", pCls->m_csName.GetBuffer() );
    AppendString( csFmt, dl_Comment, -1, NULL );
}

void CDisassemblerRTF::Disassemble( CString& csAsmRTFOut, DBGLINES_ARRAY& dbgLinesOut )
{
    m_pLineArray = &dbgLinesOut;

    m_csAsmRTF = "{\\rtf1\\ansi\\ansicpg1251\\deff0\\deflang1049{\\fonttbl{\\f0\\fnil\\fprq13\\fcharset204{\\*\\fname Courier New;}Courier New CYR;}{\\f1\\fnil\\fprq13\\fcharset0 Courier New;}}"
        "{\\colortbl ;\\red64\\green64\\blue64;\\red0\\green0\\blue164;\\red164\\green0\\blue0;\\red0\\green124\\blue0;}"
        "\\viewkind4\\uc1\\pard\\cf1\\b\\f0\\fs20";

    // in this color table: CLR_BLACK \\cf1, CLR_BLUE  \\cf2, CLR_READ  \\cf3, CLR_GREEN \\cf4

    SYMBOLLIST listGlobal = m_pRT->get_GlobalSymbolList();
    SYMBOL_LIST::iterator theIter = listGlobal.end();
    symbolref pSym = (symbolref)theIter;

    while( pSym )
    {
        SymbolRef typeName = FindType( listGlobal, pSym->m_dwType );

        if( pSym->m_SymKind == sk_var )
        {
            DisassembleVariable( typeName, pSym, FALSE );
        }
        else
        if( pSym->m_SymKind == sk_proc )
        {
            DisassembleFunction( typeName, pSym, FALSE );
        }
        else
        if( pSym->m_SymKind == sk_obj )
        {
            DisassembleClass( pSym );
        }

        theIter--;
        pSym = theIter != NULL ? (symbolref)theIter : NULL;
    }

    AppendString( "}" ); // final

    csAsmRTFOut = m_csAsmRTF;
}

void CDisassemblerRTF::DisassembleCode( symbolref pSym )
{
    dword dwSize = pSym->m_Code.count();
    dword dwIP = 0;
    CString csFmt;

    while( dwIP < dwSize )
    {
        InstructionData *pDecData = pSym->m_Code.get( dwIP );
        if( !decode_instr_text( *pDecData ) )
        {
            AfxMessageBox( "Decode instruction fail!" );
            break;
        }

        if( pDecData->m_bInstrCode == p_label || pDecData->m_bInstrCode == p_stkvar ) {
            ASSERT(0);
        } else if( pDecData->m_bInstrCode == p_calli ) {
            symbolref pFunc = m_pRT->get_FunctionByIndex( pDecData->m_Value._u4 );
            if( pFunc ) {
                csFmt.Format( "\\cf2    \\tab %08u\\cf3\\tab %s \\tab\\tab\\tab\\cf1 ; %s\\par\r\n", dwIP, pDecData->m_strInstr.GetBuffer(), pFunc->m_csName.GetBuffer() ); 
            } else {
                csFmt.Format( "\\cf2    \\tab %08u\\cf3\\tab %s\\par\r\n", dwIP, pDecData->m_strInstr.GetBuffer() ); 
            }
        }
        else if( pDecData->m_bInstrCode == p_load && (pDecData->m_bTypeAddr & 0x0F) == a_m )
        { // problem: need to escape several characters for RTF
            SymbolRef varSym = FindGlobalVarByIndex( m_pRT->get_GlobalSymbolList(), pDecData->m_Value._u4 ); 
            if( !varSym.isNULL() )
            {
                XortElement *pVal = m_pRT->get_GlobalVariableByIndex( pDecData->m_Value._u4 );
                string csVal = xFormatElementValue( pVal ? *pVal : varSym.m_Value, 16 );
                CString csEscaped;
                EscapeRTFString( &csVal, csEscaped );
                //TRACE( "%s [---] %s\n", csVal.GetBuffer(), csEscaped );
                csFmt.Format( "\\cf2    \\tab %08u\\cf3\\tab %s \\tab\\tab\\cf1 ; %s = %s\\par\r\n", dwIP, pDecData->m_strInstr.GetBuffer(), varSym.m_csName.GetBuffer(), csEscaped );
            } else {
                csFmt.Format( "\\cf2    \\tab %08u\\cf3\\tab %s\\par\r\n", dwIP, pDecData->m_strInstr.GetBuffer() ); 
            }
        } else {
            csFmt.Format( "\\cf2    \\tab %08u\\cf3\\tab %s\\par\r\n", dwIP, pDecData->m_strInstr.GetBuffer() ); 
        }

        AppendString( csFmt, dl_AsmCode, dwIP, NULL, pSym );

        dwIP += 1;
    }
}
